package dyncallback

/*
#include "../c/dyncallback/dyncall_callback.h"
#include "bridge/_bridge_.h"
extern char goCallback (DCCallback* pcb, DCArgs* args, DCValue* result, void* userdata);
DCCallbackHandler* return_goCallback_point(){
	return &goCallback;
}
extern struct test globol_test;
#cgo CFLAGS: -I ${SRCDIR}/../c/dyncall
#cgo LDFLAGS: -L${SRCDIR}/ -ldyncallback
*/
import "C"
import (
	"gitee.com/yonoz/go_dyncall/model"
	"runtime"
	"strconv"
	"unsafe"
)

var functionInfos = make(map[string]*FunctionInfo)

type FunctionInfo struct {
	function   CallbackFunction
	argsTypes  []int32
	returnType int32
	userData   *int
}

func (receiver FunctionInfo) GetFunction() CallbackFunction {
	return receiver.function
}
func (receiver FunctionInfo) GetArgTypes() []int32 {
	target := make([]int32, len(receiver.argsTypes))
	copy(target, receiver.argsTypes)
	return target
}
func (receiver FunctionInfo) GetReturnType() int32 {
	return receiver.returnType
}

type CallbackFunction func(*int, ...interface{}) interface{}
type callbackPointer struct {
	ptr  unsafe.Pointer
	key  string
	free bool
}

func (pointer callbackPointer) GetPointer() unsafe.Pointer {
	return pointer.ptr
}
func freeCallBackFunction(pointer *callbackPointer) {
	DcbFreeCallback(pointer)
}

var signatureChars = []rune{
	rune(C.DC_SIGCHAR_VOID),
	rune(C.DC_SIGCHAR_BOOL),
	rune(C.DC_SIGCHAR_CHAR),
	rune(C.DC_SIGCHAR_CHAR),
	rune(C.DC_SIGCHAR_UCHAR),
	rune(C.DC_SIGCHAR_SHORT),
	rune(C.DC_SIGCHAR_USHORT),
	rune(C.DC_SIGCHAR_INT),
	rune(C.DC_SIGCHAR_UINT),
	rune(C.DC_SIGCHAR_LONG),
	rune(C.DC_SIGCHAR_ULONG),
	//rune(C.DC_SIGCHAR_LONGLONG),
	//rune(C.DC_SIGCHAR_ULONGLONG),
	rune(C.DC_SIGCHAR_FLOAT),
	rune(C.DC_SIGCHAR_DOUBLE),
	rune(C.DC_SIGCHAR_POINTER),
	rune(C.DC_SIGCHAR_STRING),
	rune(C.DC_SIGCHAR_STRUCT),
	rune(C.DC_SIGCHAR_ENDARG),
}

func buildSignature(returnType int32, argsType []int32) string {
	result := ""
	for i, length := 0, len(argsType); i < length; i++ {
		result += string(signatureChars[argsType[i]])
	}
	result += string(signatureChars[len(signatureChars)-1]) + string(signatureChars[returnType])
	return result
}

//go:generate go run ../install/main.go ${CC} -O0 -c bridge/_bridge_.S -o ./dyncallback
func DcbNewCallback(callback CallbackFunction, userData *int, returnType int32, argsTypes ...int32) model.Pointer {
	key := strconv.Itoa(*((*int)(unsafe.Pointer(&callback)))) + "_" + strconv.Itoa(*userData)
	signature := buildSignature(returnType, argsTypes)
	functionInfos[key] = &FunctionInfo{function: callback, argsTypes: argsTypes, userData: userData, returnType: returnType}
	result := C.dcbNewCallback(C.CString(signature), C.return_goCallback_point(), unsafe.Pointer(C.CString(key)))
	pointer := &callbackPointer{ptr: unsafe.Pointer(result), key: key}
	runtime.SetFinalizer(pointer, freeCallBackFunction)
	return pointer
}
func DcbInitCallback(pointer model.Pointer, callback CallbackFunction, userData *int, returnType int32, argsTypes ...int32) {
	currentPointer, ok := pointer.(callbackPointer)
	if !ok || currentPointer.free {
		return
	}
	delete(functionInfos, currentPointer.key)
	key := strconv.Itoa(*((*int)(unsafe.Pointer(&callback)))) + "_" + strconv.Itoa(*userData)
	signature := buildSignature(returnType, argsTypes)
	functionInfos[key] = &FunctionInfo{function: callback, userData: userData, argsTypes: argsTypes, returnType: returnType}
	C.dcbInitCallback((*C.DCCallback)(currentPointer.ptr), C.CString(signature), C.return_goCallback_point(), unsafe.Pointer(&callback))
	currentPointer.key = key
}
func DcbFreeCallback(pointer model.Pointer) {
	currentPointer, ok := pointer.(callbackPointer)
	if !ok || currentPointer.free {
		return
	}
	currentPointer.free = true
	delete(functionInfos, currentPointer.key)
	C.dcbFreeCallback((*C.DCCallback)(currentPointer.ptr))
}
func DcbGetFunctionInfo(pointer model.Pointer) *FunctionInfo {
	currentPointer, ok := pointer.(callbackPointer)
	if !ok || currentPointer.free {
		return nil
	}
	return functionInfos[currentPointer.key]
}
